import com.bulenkov.darcula.DarculaLaf;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.Socket;
import java.util.ArrayList;

import static java.lang.Thread.interrupted;

/**
 * Created by LLAP on 2017/12/5.
 * Copyright (c) 2017 LLAP. All rights reserved.
 */
public class Worker extends JFrame {
    private String queueName;
    private String workerName = "";
    public String getWorkerName() {
        return workerName;
    }
    private String targetIP;
    private int requestPort = 1926;
    private int queueNo;
    private int workerID = 0;
    private boolean assist = false;

    private Thread listener;

    public boolean isAssist() {
        return assist;
    }

    public void setAssist(boolean assist) {
        this.assist = assist;
    }

    private String multiCastIP = "224.116.8.1";
    private int port = 817;
    private InetAddress group;
    private MulticastSocket msocket;
    private DatagramPacket pack;
    byte[] buf;
    public Logger logger;

    private JPanel panel;
    private JLabel current;
    private JLabel wait;
    private JButton call;
    private JButton settings;

    public Worker() {
        super("Worker");
        try {
            UIManager.setLookAndFeel(new DarculaLaf());
        } catch (Exception e) {
            try {
                UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
            }
            catch (Exception ex) {
                try {
                    UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
                }
                catch (Exception exc) {
                    System.out.println("Substance Raven Graphite failed to initialize");
                }
            }
        }
        this.setSize(550, 150);
        this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        this.setLayout(new BorderLayout());
        logger = new Logger("Worker");
        panel = new JPanel();
        panel.setBorder(new EmptyBorder(15, 15, 15, 15));
        panel.setLayout(new GridLayout(1, 4, 15, 0));

        current = new JLabel("空队列");
        current.setFont(new java.awt.Font("Microsoft Yahei UI", 0, 30));
        wait = new JLabel("当前等待：0人");
        call = new JButton("叫号");
        settings = new JButton("设置");

        call.setEnabled(false);

        panel.add(current);
        panel.add(wait);
        panel.add(call);
        panel.add(settings);

        settings.addActionListener(e -> {
            new Settings(this).setVisible(true);
        });
        call.addActionListener(e -> {
            call();
        });
        this.add(panel);
        //new Settings(this).setVisible(true);
    }

    public void call() {
        /* Args: %worker%/%act%/%queueNo%[/%workerName% | /%priority%]
         *  Example: 42/poll/一号窗口/1
         *  Example: 51/add/2
         *  Example: 51/add/2/1
         *
         */
        if (targetIP == "") return;
        String request = String.format("%d/poll/%d/%s", workerID, queueNo, (workerName.equals("") ? "默认窗口" : workerName));
        try {
            Socket socket = new Socket(targetIP, requestPort);
            PrintWriter sout = new PrintWriter(socket.getOutputStream(), false);
            sout.println(request);
            sout.flush();
            BufferedReader callback = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String response = callback.readLine();
            if (response != null && !response.equals("")) current.setText(response);
            else current.setText("空闲");
            logger.log(workerName + "叫号" + response);
            callback.close();
            sout.close();
            socket.close();
        }
        catch (Exception ex) {
            System.out.println("Request Error: " + ex.getMessage());
        }
    }

    public void startCall() {
        call.setEnabled(true);
        if (listener != null) {
            if (listener.isAlive()) listener.interrupt();
        }
        listener = new Thread(() -> {
            try {
                msocket = new MulticastSocket(port);
                group = InetAddress.getByName(multiCastIP);
                msocket.joinGroup(group);
                buf = new byte[512];
                pack = new DatagramPacket(buf, buf.length, group, port);
                while(!interrupted()) {
                    try {
                        if (interrupted()) break;
                        msocket.receive(pack);
                        if (interrupted()) break;
                        String message = new String(pack.getData(), 0, pack.getLength());
                        String[] list = message.split("\\|");
                        if (list.length <= queueNo) {
                            JOptionPane.showMessageDialog(null, "队列失效，请重新设置");
                            dispose();
                            new Worker().setVisible(true);
                            return;
                        }
                        String[] args = list[queueNo].split(",");
                        int waitCount = Integer.parseInt(args[1]);
                        wait.setText("<html>等待人数：" + waitCount);
                        if (assist && waitCount <= 0) {
                            wait.setText(wait.getText() + "<br>协助其他队列中");
                        }
                        wait.setText(wait.getText() + "</html>");
                        if (!assist && waitCount <= 0) this.call.setEnabled(false);
                        else this.call.setEnabled(true);
                        this.setTitle(workerName + "@" + args[0]);
                    }
                    catch (Exception e) {}
                }
            }
            catch (Exception ex) {
                System.out.println(ex.getMessage());
            }
        });
        listener.start();
    }
    public void setQueueName(String queueName) {
        this.queueName = queueName;
    }

    public void setTargetIP(String targetIP) {
        this.targetIP = targetIP;
    }

    public void setQueueNo(int queueNo) {
        this.queueNo = queueNo;
    }

    public void setWorkerID(int workerID) {
        this.workerID = workerID;
    }

    public void setWorkerName(String workerName) {
        this.workerName = workerName;
    }
}

class Settings extends JFrame {
    private Worker w;
    private JPanel queuePanel;
    private JPanel promptPanel;
    private JPanel workerPanel;
    private JCheckBox assist;
    private JTextField workerName;
    private ArrayList<JRadioButton> queueList;
    private JButton buttonConfirm, buttonCancel;
    private String multiCastIP = "224.116.8.1";
    private int port = 817;
    private InetAddress group;
    private MulticastSocket msocket;
    private DatagramPacket pack;
    private ButtonGroup buttons;
    byte[] buf;

    public Settings(Worker w) {
        super();
        this.setLayout(new BorderLayout(0, 15));
        this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        this.w = w;
        queueList = new ArrayList<>();
        queuePanel = new JPanel();
        promptPanel = new JPanel(new GridLayout(1, 3));
        workerPanel = new JPanel(new BorderLayout(10, 0));

        queuePanel.setBorder(new EmptyBorder(20, 20, 20, 20));
        promptPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
        workerPanel.setBorder(new EmptyBorder(15, 30, 15, 30));
        workerName = new JTextField(w.getWorkerName());
        workerPanel.add("West", new JLabel("窗口名称", SwingConstants.RIGHT));
        workerPanel.add("Center", workerName);
        buttonCancel = new JButton("取消");
        buttonConfirm = new JButton("确定");
        assist = new JCheckBox("协助处理其他队列", this.w.isAssist());
        promptPanel.add(assist);
        promptPanel.add(buttonConfirm);
        promptPanel.add(buttonCancel);
        buttonCancel.addActionListener(e -> {
            dispose();
        });
        buttonConfirm.addActionListener(e -> {
            JRadioButton selected = null;
            for (JRadioButton i : queueList) {
                if (i.isSelected()) {
                    selected = i;
                    break;
                }
            }
            if (selected == null) return;
            try {
                w.setWorkerID(Integer.parseInt(InetAddress.getLocalHost().getHostAddress().split("\\.")[3]) & 0x3ff);
            }
            catch (Exception ex) {
                System.out.println(ex.getMessage());
                w.setWorkerID(0);
            }
            w.setQueueName(selected.getText());
            w.setQueueNo(queueList.indexOf(selected));
            w.setTitle(this.workerName.getText() + "@" + selected.getText());
            w.setWorkerName(this.workerName.getText());
            w.setAssist(this.assist.isSelected());
            w.logger.log(this.workerName.getText() + "绑定队列" + (char)('A' + queueList.indexOf(selected)));
            w.startCall();
            dispose();
        });
        buttons = new ButtonGroup();
        connect();
        try {
            msocket.receive(pack);
            String message = new String(pack.getData(), 0, pack.getLength());
            String[] list = message.split("\\|");
            if (list.length <= 1) {
                JOptionPane.showMessageDialog(null, "暂无可用队列");
                dispose();
                System.gc();
                return;
            }
            w.setTargetIP(list[list.length - 1]);
            this.setTitle(list[list.length - 1]);
            queuePanel.setLayout(new GridLayout(list.length - 1, 1, 0, 15));
            for (int i = 0; i < list.length - 1; i++) {
                String[] args = list[i].split(",");
                queueList.add(new JRadioButton(args[0]));
                queuePanel.add(queueList.get(queueList.size() - 1));
                buttons.add(queueList.get(queueList.size() - 1));
            }
            queuePanel.updateUI();
        }
        catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
        this.add(workerPanel, BorderLayout.NORTH);
        this.add(queuePanel, BorderLayout.CENTER);
        this.add(promptPanel, BorderLayout.SOUTH);
        this.pack();
        this.setSize(450, this.getHeight());
    }

    public void connect() {
        try {
            msocket = new MulticastSocket(port);
            group = InetAddress.getByName(multiCastIP);
            msocket.joinGroup(group);
            buf = new byte[512];
            pack = new DatagramPacket(buf, buf.length, group, port);
        }
        catch (Exception e) {}
    }
}
